/*
 * File Name:               VirtualJoystick_Moved.cs
 * 
 * Description:             普通类
 * Author:                  lisiyu <576603306@qq.com>

 * Create Date:             2017/04/24
 */

namespace UnityEngine.UI
{
    using UnityEngine;
    using EventSystems;
    using System;
    using System.Collections.Generic;

    [AddComponentMenu("UI/VirtualJoystick_Moved", 70)]
    [DisallowMultipleComponent]
    [RequireComponent(typeof(Graphic))]
    public class VirtualJoystick_Moved : IVirtualJoystick, IPointerDownHandler, IPointerUpHandler, IDragHandler
    {
        public RectTransform TransFixed;                // 固定节点
        public RectTransform TransMoved;                // 移动节点
        public RectTransform TransArea;                 // 初始区域
        public RectTransform TransDragArea;             // 拖拽距离限制
        public RectTransform TransArrow;                // 箭头
        private float mMin_x, mMax_x, mMin_y, mMax_y;   // 初始区域

        private float mMaxDistance;                     // 移动节点与固定节点的最大距离
        private Vector2 mPointerDownPos;                // 按下时的位置

        private Vector2 m_InitedPos;                    // 固定节点的世界坐标

        private List<Graphic> mChildGraphics;           // 所有图形
        private List<Graphic> mArrowGraphics;           // 箭头图形
        private readonly Color ColorHideArrow = new Color(1, 1, 1, 0f);
        private readonly Color ColorHide = new Color(1, 1, 1, 0.5f);
        private readonly Color ColorShow = new Color(1, 1, 1, 1f);

        private bool inited = false;

        void Awake()
        {
            mChildGraphics = new List<Graphic>();
            mChildGraphics.AddRange(TransFixed.GetComponentsInChildren<Graphic>());
            mChildGraphics.AddRange(TransMoved.GetComponentsInChildren<Graphic>());

            mArrowGraphics = new List<Graphic>();
            mArrowGraphics.AddRange(TransArrow.GetComponentsInChildren<Graphic>());

            showOrHide(false);
        }

        private void initData()
        {
            inited = true;
            Vector3[] tmpAreaRect = new Vector3[4];
            TransArea.GetWorldCorners(tmpAreaRect);

            var tmpAreaMin = UIManager.Singleton.WorldToScreenPoint(new Vector3(tmpAreaRect[0].x, tmpAreaRect[0].y, TransArea.position.z));
            var tmpAreaMax = UIManager.Singleton.WorldToScreenPoint(new Vector3(tmpAreaRect[2].x, tmpAreaRect[1].y, TransArea.position.z));
            mMin_x = tmpAreaMin.x;
            mMin_y = tmpAreaMin.y;
            mMax_x = tmpAreaMax.x;
            mMax_y = tmpAreaMax.y;

            TransDragArea.GetWorldCorners(tmpAreaRect);
            var transDragMin = UIManager.Singleton.WorldToScreenPoint(new Vector3(tmpAreaRect[0].x, tmpAreaRect[0].y, TransDragArea.position.z));
            var transDragMax = UIManager.Singleton.WorldToScreenPoint(new Vector3(tmpAreaRect[1].x, tmpAreaRect[1].y, TransDragArea.position.z));
            mMaxDistance = transDragMax.y - transDragMin.y;

            m_InitedPos = UIManager.Singleton.WorldToScreenPoint(TransFixed.position);
        }

        public void OnDrag(PointerEventData eventData)
        {
            setMovedPos_ByScreen(eventData.position);
        }

        public void OnPointerDown(PointerEventData eventData)
        {
            if (!inited) initData();

            mPointerDownPos = eventData.position;

            if (mPointerDownPos.x < mMin_x)
                mPointerDownPos.x = mMin_x;
            else if (mPointerDownPos.x > mMax_x)
                mPointerDownPos.x = mMax_x;

            if (mPointerDownPos.y < mMin_y)
                mPointerDownPos.y = mMin_y;
            else if (mPointerDownPos.y > mMax_y)
                mPointerDownPos.y = mMax_y;

            showOrHide(true);

            setFixedPos_ByScreen(mPointerDownPos);
            setMovedPos_ByScreen(eventData.position);
        }

        public void OnPointerUp(PointerEventData eventData)
        {
            showOrHide(false);

            TransFixed.position = UIManager.Singleton.ScreenToWorldPoint(m_InitedPos, TransFixed.position.z);
            TransMoved.position = UIManager.Singleton.ScreenToWorldPoint(m_InitedPos, TransMoved.position.z);

            if (On_JoystickMoveEnd != null)
                On_JoystickMoveEnd();
        }

        private void showOrHide(bool show)
        {
            var color = show ? ColorShow : ColorHide;
            foreach (var tmp in mChildGraphics)
            {
                tmp.color = color;
            }

            color = show ? ColorShow : ColorHideArrow;
            foreach (var tmp in mArrowGraphics)
            {
                tmp.color = color;
            }
        }

        private void setFixedPos_ByScreen(Vector2 worldPos)
        {
            if (TransFixed == null) return;
            TransFixed.position = UIManager.Singleton.ScreenToWorldPoint(worldPos, TransFixed.position.z);
        }

        private void setMovedPos_ByScreen(Vector2 screenPos)
        {
            if (TransMoved == null) return;

            var dir = (screenPos - mPointerDownPos);
            if (dir.magnitude > mMaxDistance)
            {
                dir = (screenPos - mPointerDownPos).normalized * mMaxDistance;
            }
            else if (dir.magnitude < 30)
            {
                dir = Vector2.zero;
            }

            var retPos = mPointerDownPos + dir;
            TransMoved.position = UIManager.Singleton.ScreenToWorldPoint(retPos, TransMoved.position.z);

            if (On_JoystickMove != null)
                On_JoystickMove(dir);

            var angle = Vector2.Angle(dir, XVector2.up);
            if (dir.x > 0)
                angle *= -1;
            TransArrow.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
        }

        void OnDisableInput()
        {
            if (!inited) return;
            OnPointerUp(null);
        }
    }
}